import openai
import httpx
import os
import sys
import json
import subprocess
import random
from pathlib import Path
import re
#import tiktoken
import time
import difflib
from pprint import pprint
from rag.rag import query_rag

cur_dir = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[:-1])
json_path = os.path.join(cur_dir, "icl-demonstrations")
code_example_path = os.path.join(cur_dir, "icl-demonstrations")

c_pro_name = "02-Medium"

rust_pro_name = "rust-pro"

code_path = os.path.join(cur_dir, "../Input/", c_pro_name, "src")
test_path = os.path.join(cur_dir, "Intermediate-Result")
llm_path = os.path.join(cur_dir, f"llm-{c_pro_name}-output")

if not os.path.exists(llm_path):
    os.makedirs(llm_path)

LLM_MODEL="o1-mini"

pass_filename = f"llm-{c_pro_name}-test_pass.txt"
syntax_pass_path = f"llm-{c_pro_name}-syntax_pass.txt"
syntax_pass_file = open(syntax_pass_path, "a")

semantic_pass_path = f"llm-{c_pro_name}-semantic_pass.txt"
semantic_pass_file = open(semantic_pass_path, "a")

# CoT prompt steps
header_cot_steps = [
    "Step 1: Identify and translate struct definitions in C to Rust, preserving type accuracy.",
    "Step 2: Translate C function declarations to Rust function with empty body, adjusting syntax and memory management as needed.",
    "Step 3: Handle pointers in C by converting them to Rust references or smart pointers.",
    "Step 4: Ensure all C-to-Rust variable types match by adjusting integer sizes, using Rust-compatible data types.",
    "Step 5: Do not use extern \"C\" block to encapsulate the Rust declarations."
]

source_cot_steps = [
    "Step 1: Identify all struct definitions and functions that operate on these structs in the provided C code.",
    #"Step 2: Convert the identified C functions into methods within an `impl` block for the equivalent Rust struct.",
    "Step 2: Translate a C function into a Rust function of the same name, without changing the function name.",
    "Step 3: Ensure the Rust functions use safe Rust features and avoid `unsafe` operations. Use Rust's ownership and borrowing rules to manage memory.",
    "Step 4: Use Rust's memory safety mechanisms (ownership and borrowing) where possible.",
    "Step 5: Identify and add lifetime annotations where necessary to ensure Rust's bowrrow checker can manage reference correctly.",
    "Step 6: Use the provided necessary global symbols (structs, enums, unions, type aliases, global variables, macros) used within the functions",
    "Step 7: Ensure the generated Rust code uses safe Rust features and avoids `unsafe` operations wherever possible.",
    "Step 8: Ensure the generated Rust code is well-formated and includes appropriate comments for clarity.",
    "Step 9: For functions that reference other functions, use the provided Rust function declarations to construct function call statements.",
    "Step 10: Do not add any additional `main` function or unnecessary struct definitions. The translation should strictly focus on converting the provided C functions and their dependencies into equivalent Rust code.",
    "Step 11: Ensure that the generated Rust code does not introduce any new global variables or macros unless explicitly provided in the input symbols.",
    "Step 12: Encapsulation of methods into impl blocks is forbidden, and all C functions are translated directly into Rust functions!",
]

def get_code_snippet_pair(pair):
    snippets = "C-Rust code snippets:\n"
    idx_lst = sorted([int(idx) for idx in pair.keys()])
    rust_key = "rust"
    for idx in idx_lst:
        code = pair[str(idx)]
        snippets += f"code snippet {idx}:\n"
        c_code = code["c"]
        rs_code = code[rust_key]
        snippets += f"C code snippet:\n"
        snippets += "```c\n"
        snippets += f"{c_code}"
        snippets += "```\n"
        snippets += f"Rust code snippet:\n"
        snippets += "```rust\n"
        snippets += f"{rs_code}"
        snippets += f"```\n"
    return snippets

def get_test_code_snippets(test_json_path):
    code_snippets = []
    with open(test_json_path, "r", encoding="utf-8") as j_file:
        codes = json.load(j_file)
        c_snippet = codes["C"]
        code_snippet = c_snippet["code"]
        idx_lst = sorted([int(idx) for idx in code_snippet.keys()])

        for idx in idx_lst:
            code = code_snippet[str(idx)]
            code_snippets.append(code)
    return code_snippets

def get_main_flag(test_json_path):
    MAIN_FLAG = False
    with open(test_json_path, "r", encoding="utf-8") as j_file:
        codes = json.load(j_file)
        c_snippet = codes["C"]
        main_content = c_snippet["main"]
        if main_content == "True":
            MAIN_FLAG = True
    return MAIN_FLAG

def get_prompt_from_jsons(path, prompt, snippet_func):
    snippets = ""
    with open(path, "r", encoding="utf-8") as j_file:
        codes = json.load(j_file)
        snippet = snippet_func(codes)
        prompt += snippet
    return prompt

def read_passed_files(filename):
    """Read the list of processed file names"""
    if not Path(filename).exists():
        return set()
    with open(filename, 'r') as file:
        return set(line.strip() for line in file if line.strip())

def in_passed_files(filename, file_path):
    pass_files = read_passed_files(filename)
    for f in pass_files:
        if file_path in f:
            return True
    return False

tmp_file_name = "tmp.rs"

def check_rust_code(rust_code):
    # Write the generated Rust code to a temporary file
    with open(tmp_file_name, "w") as file:
        file.write(rust_code)

    try:
        from rust_error import cargo_check_rust_file
        errors = cargo_check_rust_file(tmp_file_name)
        if len(errors) > 0:
            if len(errors) == 1 and errors[0] == "Not invalid errors":
                return False, []
            return True, errors
        else:
            return False, []

    except Exception as e:
        return True, e

def fix_errors_with_llm(error_message, generated_rust_code, max_iterations=2):
    # Initialize variables
    corrected_rust_code = generated_rust_code
    idx = 0
    flag = False
    while len(error_message) > 0:
        iterations = 0
        error = error_message[0]
        error_message.pop(0)
        if isinstance(error, str):
            return corrected_rust_code
        error_line = error['line_number']
        idx += 1
        if idx > max_iterations:
            break
        snap = corrected_rust_code
        error_lst = [error]
        # Iteratively fix errors using GPT-4
        #while iterations < max_iterations:
        while len(error_lst) > 0:
            # Construct prompt to GPT-4
            error = error_lst[0]
            if isinstance(error, str):
                error_lst.pop(0)
                continue
            error_lst.pop(0)
            fix_idx = 0
            defense=False
            while fix_idx < 3:
                fix_idx += 1
                try:
                    prompt, start, end, original_code = construct_prompt(error, corrected_rust_code)
                    print(f"Prompt to fix errors:")
                    print(prompt)
                except ValueError:
                    #return corrected_rust_code
                    continue
                # Call GPT-4 API to generate fix
                try:
                    fix = call_llm_api(prompt)
                    defense = True
                    
                    if defense:
                        break
                except (openai.RateLimitError, openai.InternalServerError, openai.BadRequestError):
                    time.sleep(3)
                    continue
            
            iterations += 1
            if iterations > max_iterations+1:
                break
            
            if not defense:
                continue
            # Update corrected Rust code
            corrected_rust_code = apply_fix(corrected_rust_code, fix, start, end)
            # Check if there are still errors
            has_error, errors = check_rust_code(corrected_rust_code)
            #time.sleep(5)
            # If no errors found, break the loop
            if not has_error:
                flag = True
                break
            error_lst = errors
        if flag:
            break

    return corrected_rust_code

def extract_error_code_snippet(rust_file_path, line):
    code_lines = []
    SNIPPET_LEN = 10
    with open(rust_file_path, "r") as rs_file:
        lineno = 0
        for code_line in rs_file.readlines():
            lineno += 1
            code_lines.append("[%3d] %s" % (lineno, code_line))
    if len(code_lines) > line:
        code_snippet = []
        start = 0 if line-SNIPPET_LEN-1 < 0 else line-SNIPPET_LEN - 1
        end = len(code_lines) if line + SNIPPET_LEN > len(code_lines) else line + SNIPPET_LEN
        return "".join(code_snippet[start:end]), start + 1, end
    else:
        return "", line, line

def construct_prompt(error, rust_code):
    # Construct prompt for LLM
    file_path = error['file_path']
    line = error['line_number']
    column = error['column_number']
    details = error['error_details']
    description = error['description']
    details = description + details

    original_code = ""
    start, end = 0, 0
    with open(tmp_file_name, "w") as tf:
        tf.write(rust_code)

    if "unclosed delimiter" in details:
        try:
            result = subprocess.run(["./fix-unclosed-delimiter/target/debug/fix-unclosed-delimiter", tmp_file_name], capture_output=True, text=True, check=True, timeout=10)

            fixed_rust_code = result.stdout
            with open(tmp_file_name, "w") as tf:
                tf.write(fixed_rust_code)
        except subprocess.CalledProcessError as e:
            print(f"Fail to fix unclosed delimiter:\n{rust_code}")

    try:
        result = subprocess.run(["./blockfinder/target/debug/blockfinder", tmp_file_name, line], capture_output=True, text=True, check=True, timeout=10)
        code_lines = result.stdout
        start, end = 0, 0
        code_lines = code_lines.split("\n")
        original_code = []
        #print(code_lines)
        if code_lines:
            #print(code_lines[0])
            start, end = code_lines[0].split(" ")
            start = int(start)
            end = int(end)
            original_code = "\n".join(code_lines[1:])
        #print("construct", start, end, code_lines)
        #print(f"Error details:{details}")
    except subprocess.CalledProcessError as e:
        original_code = ""
    original_code_head = "OriginalCode@"
    # original code may be null if errors in Rust file.

    if original_code in ("", " ", "\n"):
        original_code, start, end = extract_error_code_snippet(tmp_file_name, int(line))
    if start == end:
        original_code_head += f"{start}"
    else:
        original_code_head += f"{start}-{end}"

    prompt = f"""You are provided with the following error from running 'rustc {tmp_file_name}' along with Rust code snippets from one '.rs' file related to this error.    
Error message:
{details}
The code block containing the erroneous lines is as follows:
{original_code_head}
{original_code}\n"""
    #rs_cfg, rs_ddg = get_rust_cfg_ddg(tmp_file_name)
    rs_cfg = ""
    rs_ddg = ""
    if rs_cfg != "" or rs_ddg != "":
        prompt += """To assist you in fixing the Rust code, I will provide you with a list of Control Flow Graph(CFG) and Data Dependency Graph(DDG) of the Rust code snippets. The format of these CFGs and DDGs is shown as follows:
CFG Format:
CFG:
Nodes:
Block id:
Code Statements
Edges:
(Block id -> Block id)
The code statements in each block are sequential. Do not split these statements into different blocks. Additionally, the "{" and "}" characters are ignored. Do not alter the code within these CFG blocks.
DDG format:
DDG:
Nodes:
id (Identifier or Statement)
Edges:
Edge from Variable or Statement to Variable or Statement
The list of CFGs, DDGs of {tmp_file_name} are shown as follows:
{rf_cfg}
{rs_ddg}
"""
    prompt += "Instructions: Fix the error in the above Rust code snippets. Not every snippet might require a fix or be relevant to the error , but consider all the code snippets as they may help you derive the best possible fix. Assume that the snippets might not be complete and could be missing lines above or below. Do not add comments or code that is unnecessary to fix the error. Do not use unsafe or unstable features (such asthrough '#![ feature (...) ]'). For your answer, return one or more ChangeLog groups, each containing one or more fixes to the above code snippets . Each group must be formatted according to the instructions below.\n"
    prompt += "Format instructions: Each ChangeLog group must start with a description of the included fixes. The group must then list one or more pairs of (OriginalCode, FixedCode) snippets. Each OriginalCode snippet must list all consecutive original lines of code that must be replaced (including a few lines before and after the fixes), followed by the FixedCode snippet with all consecutive fixed lines of code that must replace the original lines of code (including the same few lines before and after the changes). In each pair, the OriginalCode and FixedCode snippets must start at the same source code line number N. Each listed code line, in both the OriginalCode and FixedCode snippets, must be prefixed with [N] to matches the line index N in the above snippets, and then be prefixed with exactly the same whitespace indentation as the original snippets above."
    prompt += """
ChangeLog :1@<file >
FixDescription : <summary >.
OriginalCode@4-6:
[4] <white space > <original code line >
[5] <white space > <original code line >
[6] <white space > <original code line >
FixedCode@4-6:
[4] <white space > <fixed code line >
[5] <white space > <fixed code line >
[6] <white space > <fixed code line >
"""
    return prompt, start, end, original_code

def call_llm_api(prompt, compile_error=False):
    response = openai_instance.chat.completions.create(
        model=LLM_MODEL,
        messages=[
            {"role": "system", "content": "You are now acting as an expert in Rust programming languages. You are good at fixing Rust program errors."},
            {"role": "user", "content": prompt}
        ]
    )

    # Extract fix from GPT-4 response
    fix = response.choices[0].message.content
    print(f"Fixed code:\n{fix}")
    return fix

def extract_original_code_blocks(text):
    # Regular expression used to split each OriginalCode block
    changelog_blocks = re.split(r'\n(?=ChangeLog)', text)
    change_logs = []
    for block in changelog_blocks:
        if "OriginalCode@" not in block:
            continue
        change_logs.append(block)
    return change_logs

def extract_original_code(text):
    # Regular expressions match the number or range of numbers following 'OriginalCode@'
    # Capture the portion or file end that does not start with a blank line until the next occurrence
    pattern = re.compile(r'OriginalCode@\d+(?:-\d+)?:\n((?:\[\s*\d+\].*?(?:\n|$))+)', re.S)

    # Find all matching parts
    results = []
    line_lst = []
    start, end = 0, 0
    # Find the line number or range of line numbers after OriginalCode@
    fixed_code_pattern = re.compile(r'OriginalCode@(\d+(?:-\d+)?):')
    matches = fixed_code_pattern.finditer(text)
    start_line, end_line = 0, 0
    for match in matches:
        line_range = match.group(1)
        start_line, end_line = (line_range.split('-') + [line_range])[:2]  # Handling single lines or ranges
        start_line, end_line = int(start_line), int(end_line)
    
    for match in pattern.finditer(text):
        block = match.group(1)
        # Extract the line number and code for each line
        #lines = re.findall(r'\[(\d+)\]\s*(.*)', block)
        lines = re.findall(r'\[(\s*\d+)\](.*?)(?=\n\[|\n$|$)', block)
        for line_number, code in lines:
            line_lst.append(int(line_number))
            if not code.endswith("\n"):
                code += "\n"
            #line = f"[{line_number}] {code}"
            #results.append(line)
            results.append(code)

    return results, start_line, end_line

def extract_fixed_code_blocks(text):
    # Regular expression used to split each ChangeLog block
    changelog_blocks = re.split(r'\n(?=ChangeLog)', text)
    change_logs = []
    for block in changelog_blocks:
        if "FixedCode@" not in block:
            continue
        change_logs.append(block)
    return change_logs

def extract_fixed_code(text):
    # Regular expressions match the number or range of numbers following 'FixedCode @' 
    # Capture the portion or file end that does not start with a blank line until the next occurrence
    pattern = re.compile(r'FixedCode@\d+(?:-\d+)?:\n((?:\[\s*\d+\].*?(?:\n|$))+)', re.S)

    # Find all matching parts
    results = []
    start, end = 0, 0
    # Find all matching parts and find the line number or range of line numbers after FixedCode@
    fixed_code_pattern = re.compile(r'FixedCode@(\d+(?:-\d+)?):')
    matches = fixed_code_pattern.finditer(text)
    start_line, end_line = 0, 0
    for match in matches:
        line_range = match.group(1)
        start_line, end_line = (line_range.split('-') + [line_range])[:2]  # Handling single lines or ranges
        start_line, end_line = int(start_line), int(end_line)
    
    for match in pattern.finditer(text):
        block = match.group(1)
        # Extract the line number and code for each line
        #lines = re.findall(r'\[(\d+)\]\s*(.*)', block)
        lines = re.findall(r'\[(\s*\d+)\](.*?)(?=\n\[|\n$|$)', block)
        for line_number, code in lines:
            if not code.endswith("\n"):
                code += "\n"
            results.append(code)

    return results, start_line, end_line

def apply_fix(rust_code, fix, start, end):
    # Apply fix to the Rust code
    corrected_rust_code = rust_code
    #code_snippets = rust_code 
    # write to the temporary file and then read it to ignore split the escape character "\n" in the constant string.
    with open(tmp_file_name, "w") as tmp_file:
        tmp_file.write(corrected_rust_code)

    with open(tmp_file_name, "r") as tmp_file:
        code_snippets = tmp_file.readlines()
    changelogs = extract_fixed_code_blocks(fix)
    original_changelogs = extract_original_code_blocks(fix)
    if len(changelogs) != len(original_changelogs):
        return "".join(code_snippets)
    for changelog, original_changelog in zip(changelogs, original_changelogs):
        fix_code, start_line, end_line = extract_fixed_code(changelog)
        original_code, original_start_line, original_end_line = extract_original_code(original_changelog)
        # defensive programming, the original code shall be same with the code snippets
        print(f"GPT returns the original code:\n{original_code}")
        if original_start_line != start_line or original_end_line != end_line:
            continue
        if len(original_code) != original_end_line-original_start_line+1 or \
                len(code_snippets) < original_end_line:
            continue
        print(f"fixed_code:{fix_code}, {start_line, end_line}, {code_snippets}")
        if len(fix_code) != (end_line-start_line+1) or len(code_snippets) < end_line:
            continue

        for i in range(start_line-1, end_line):
            print(i, i+1-start_line, len(code_snippets), len(fix_code))
            code_snippets[i] = fix_code[i+1-start_line]
    corrected_rust_code = "".join(code_snippets)
    print(f"After fix:\n{corrected_rust_code}")
    return corrected_rust_code

# Define custom parameters
custom_base_url = 'https://api.xty.app/v1'

# Transfer API KEY
custom_api_key = 'sk-TfvVImBDiombumqk61317e0964934bE19e0d6f4851382f84'
custom_http_client = httpx.Client(
        base_url=custom_base_url,
        follow_redirects=True,
    )  # Replace None with your custom HTTP client if needed

# Set up OpenAI instance with custom parameters
openai_instance = openai.OpenAI(
    base_url=custom_base_url,
    api_key=custom_api_key,
    http_client=custom_http_client
)

def check_symbol(gen_rust_code, new_rust_code):
    gen_rust_file = "tmp_gen_rust_code.rs"
    new_rust_file = "tmp_new_rust_code.rs"
    print(f"Check symbol:{gen_rust_code}\n{new_rust_code}")
    with open(gen_rust_file, "w") as gen_file, open(new_rust_file, "w") as new_file:
        gen_file.write(gen_rust_code)
        new_file.write(new_rust_code)

    try:
        result = subprocess.run(["./symbol-table/target/debug/symbol-table", gen_rust_file, new_rust_file], capture_output=True, text=True, timeout=10)
    except subprocess.TimeoutExpired:
        return gen_rust_code, new_rust_code
    if result.returncode != 0:
        print(f"stderr in check_symbol:{result.stderr}")
        return gen_rust_code, new_rust_code
    output = result.stdout
    fixed_code, context_code = output.split('---SPLIT---')
    return context_code, fixed_code


def get_icl_prompt():
    prompt = """Translate the provided C code snippet into safe Rust. Following these guidelines:
1. **Functionality**: Ensure all `printf` statements in the C code are translated into `println!` or `print!` statements in Rust with equivalent functionality.
2. **Naming Conversion*: Maintain the same `function names` and `variable names` in the Rust code as in the C code. Do not change the `naming conventions`.
3. **Demonstration Examples**: Use the provided C-Rust code snippet pairs to assist with the translation. Each pair is syntactically and semantically equivalent. 
4. **C Struct Functions Translation**: If the C function is operation on a specific C struct, convert the function into methods within an `impl` block for the equivalent Rust struct."""

#     eidx = 0
#     #j_file_list = os.listdir(json_path)
#     j_file_list = [jfile for jfile in os.listdir(json_path) if jfile.endswith(".json") and not jfile.endswith("_new.json")]
#     random.shuffle(j_file_list)
#     for j_file in j_file_list:
#         eidx += 1
#         prompt += f"C-Rust example {eidx}:\n"
#         new_file = j_file.replace(".json", "_new.json")
#         new_path = os.path.join(code_example_path, new_file)
#         prompt = get_prompt_from_jsons(new_path, prompt, get_code_snippet_pair)
#         if eidx > 20:
#             break

#     prompt += """### Complex C Macro Definition Translation Example:
# C Macro Definition:
# ```c
# #include <stdio.h>

# #define PRINT_MSG(msg) do { \\
#     if (msg) { \\
#         printf("Message: %s\\n", msg); \\
#     } else { \\
#         printf("No message provided.\\n"); \\
#     } \\
# } while (0)
# ```
# Corresponding Rust Macro Definition:
# ```rust
# macro_rules! print_msg {
#     ($msg:expr) => {
#         if !$msg.is_empty() {
#             println!("Message: {}", $msg);
#         } else {
#             println!("No message provided.");
#         }
#     };
# }
# ```
# ### Simple C Macro Definition Translation Example:
# C Macro Definition:
# ```c
# #define SQUARE(x) ((x) * (x))
# #define MAX(a, b) ((a) > (b) ? (a) : (b))
# ```
# Corresponding Rust Macro Definition:
# ```rust
# const PI: f64 = 3.141592653589793;
# const MAX_BUFFER_SIZE: usize = 1024;
# ```
# """
#     prompt += """
# ### C Struct Function Translation Example:

# C strcut and function:
# typedef struct {
#     int x;
#     int y;
# } Point;

# Point init_point(Point *p, int x, int y) {
#     p->x = x;
#     p->y = y;
# }

# Point* add_points(Point *p1, const Point *p2) {
#     p1->x += p2->x;
#     p1->y += p2->y;
#     return p1;
# }

# Rust struct and impl block:
# #[derive(Debug, Clone, Copy)]
# struct Point {
#     x: i32,
#     y: i32,
# }

# impl Point {
#     fn init_point(p: &mut Self, x: i32, y: i32) {
#         p.x = x;
#         p.y = y;
#     }

#     fn add_points<'a>(p1: &'a mut Self, p2: &Self) -> &'a mut Self {
#         p1.x += p2.x;
#         p1.y += p2.y;
#         p1
#     }
# }
# """
#     prompt += """
# ### C Main Function Translation Exmaple:

# C main function code:
# ```c
# #include <stdio.h>

# int main(int argc, char *argv[]) {
#     if (argc < 2) {
#         printf("Usage: %s <filename>\\n", argv[0]);
#         return 1;
#     }
#     printf("The first argument is: %s\\n", argv[1]);
#     return 0;
# }
# ```
# Corresponding Rust Main Function by adding `use std::env`:
# ```rust
# use std::env;

# fn main() {
#     let args: Vec<String> = env::args().collect();
#     if args.len() < 2 {
#         println!("Usage: {} <filename>", args[0]);
#         return;
#     }
#     println!("The first argument is: {}", args[1]);
# }
# ```
#     """
    
#     prompt += """
# ### C Pointers and Rust Pointers Translation Examples:
# Example 1:
# C Pointer:
# ```c
# void* void_ptr = NULL;
# int value = 42;
# void_ptr = &value;
# int* int_ptr = (int*)void_ptr;
# printf("%d\n", *int_ptr);
# ```
# Rust Pointer:
# ```rust
# use std::ffi::c_void;

# fn main() {
#     let value: i32 = 42;
#     let void_ptr: *mut c_void = &value as *const i32 as *mut c_void;

#     // Cast `*mut c_void` to `*mut i32`
#     let int_ptr: *mut i32 = void_ptr as *mut i32;

#     unsafe {
#         println!("Value: {}", *int_ptr); // Output: Value: 42
#     }
# }
# ```
# Example 2:
# C Pointer:
# ```c
# struct MyStruct {
#     int a;
#     double b;
# };

# struct MyStruct my_struct = {42, 3.14};
# void* void_ptr = &my_struct;
# struct MyStruct* struct_ptr = (struct MyStruct*)void_ptr;
# printf("a: %d, b: %f\n", struct_ptr->a, struct_ptr->b);
# ```
# Rust Pointer:
# ```rust
# #[repr(C)]
# struct MyStruct {
#     a: i32,
#     b: f64,
# }

# fn main() {
#     let my_struct = MyStruct { a: 42, b: 3.14 };
#     let void_ptr: *mut std::ffi::c_void = &my_struct as *const MyStruct as *mut std::ffi::c_void;

#     // Cast `*mut c_void` to `*mut MyStruct`
#     let struct_ptr: *mut MyStruct = void_ptr as *mut MyStruct;

#     unsafe {
#         println!("a: {}, b: {}", (*struct_ptr).a, (*struct_ptr).b); // Output: a: 42, b: 3.14
#     }
# }
# ```
# Example 3:
# C Pointer:
# ```c
# int add(int a, int b) {
#     return a + b;
# }

# void* func_ptr = (void*)add;
# int (*add_ptr)(int, int) = (int (*)(int, int))func_ptr;
# printf("%d\n", add_ptr(3, 4));
# ```
# Rust Pointer:
# ```rust
# fn add(a: i32, b: i32) -> i32 {
#     a + b
# }

# fn main() {
#     let func_ptr: fn(i32, i32) -> i32 = add;
#     let void_ptr: *mut std::ffi::c_void = func_ptr as *mut std::ffi::c_void;

#     // Cast `*mut c_void` back to fn(i32, i32) -> i32`
#     let add_ptr: extern "C" fn(i32, i32) -> i32 = unsafe { std::mem::transmute(void_ptr) };

#     println!("Result: {}", add_ptr(3, 4)); // Output: Result: 7
# }
# ```
# Example 4:
# C Pointer:
# ```c
# int arr[3] = {1, 2, 3};
# void* void_ptr = arr;
# int* int_ptr = (int*)void_ptr;
# for (int i = 0; i < 3; i++) {
#     printf("%d\n", int_ptr[i]);
# }
# ```
# Rust Pointer:
# ```rust
# fn main() {
#     let arr: [i32; 3] = [1, 2, 3];
#     let void_ptr: *mut std::ffi::c_void = arr.as_ptr() as *mut std::ffi::c_void;

#     // Cast `*mut c_void` to `*const i32`
#     let int_ptr: *const i32 = void_ptr as *const i32;

#     unsafe {
#         for i in 0..3 {
#             println!("{}", *int_ptr.add(i)); // Output: 1, 2, 3
#         }
#     }
# }
# ```
# Example 5:
# C Pointer:
# ```c
# double value = 3.14;
# void* void_ptr = &value;
# int* int_ptr = (int*)void_ptr;
# printf("%d\n", *int_ptr);
# ```
# Rust Pointer:
# ```rust
# fn main() {
#     let value: f64 = 3.14;
#     let void_ptr: *mut std::ffi::c_void = &value as *const f64 as *mut std::ffi::c_void;

#     // Cast `*mut c_void` to `*mut i32`
#     let int_ptr: *mut i32 = void_ptr as *mut i32;

#     unsafe {
#         println!("Value: {}", *int_ptr); // Output: Value: 1074340347 (undefined behavior)
#     }
# }
# ```
# """

    return prompt

def get_rag_prompt(code_snippet):
    prompt = """Below are example code translation pairs (format: C code => Rust code) for reference. Please study these patterns carefully:
    ### Example C-Rust Code Pairs\n"""

    rag_results = query_rag(code_snippet)
    for code_pair in rag_results:
        c_code = code_pair['c_code']
        rust_code = code_pair['rust_code']
        prompt += f"Example C Code:\n```c\n{c_code}\n```\nExample Rust Code:\n```rust\n{rust_code}\n```\n"
    return prompt

def get_cot_instructions(cot_steps, is_head):
    prompt = """Instructions:\n"""
    # CoT prompt
    for cot_step in cot_steps:
        prompt += cot_step + "\n"

#     prompt += "\nLet's think step-by-step and provide reasoning at each stage of the translation.\n"
           
#     prompt += """### Translated Rust Format Example:"""
#     prompt+="""
# Formatted Rust code:
# ```rust
# fn pancake(n: i32) -> i32 {
#     let mut gap = 2;
#     let mut sum = 2;
#     let mut adj = -1;
    
#     while sum < n {
#         adj += 1;
#         gap = gap * 2 - 1;
#         sum += gap;
#     }
    
#     n + adj
# }

# fn foo() {
#     for i in 0..4 {
#         for j in 1..6 {
#             let n = i * 5 + j;
#             print!("p({:2}) = {:2}  ", n, pancake(n));
#         }
#         println!();
#     }
# }\n
# ```
# """

    if is_head:
        prompt += "\nYou can observe and analyse the given C header code first, understanding the difficult points in translation process. You will find the experience you learned from the provided examples is quite helpful.\n"
        prompt += "After effective and rational thinking, you can come up with detailed translation steps. Then begin the translation process according to the steps. Specifically:\n"
        prompt += """
1. **Analyze the C code** and summarize its key characteristics:
- Identify main data structures and their usage.
- Explain memory management patterns.
- Highlight any C-specific idioms or features used.

2. **Outline the translation steps** to Rust:
- Map C data structures to Rust equivalents.
- Adapt memory management to Rust's ownership and borrowing mechanisms.
- Address any C-specific features with appropriate Rust patterns.

3. **Provide the Rust translation** of the given C code:
- Ensure functionality remains consistent.
- Use idiomatic Rust code.
- Include comments explaining key translation decisions.
"""
    else:
        prompt += "\nFor each given C code snippet, you can observe and analyse the code first, understanding the difficult points in translation process. You will find the experience you learned from the provided examples is quite helpful.\n"
        prompt += "After effective and rational thinking, you can come up with detailed translation steps. Then begin the translation process according to the steps. Specifically: \n"
        prompt += """
1. **Analyze the C code** and summarize its key characteristics:
- Identify main data structures and their usage.
- Explain memory management patterns.
- Highlight any C-specific idioms or features used.

2. **Outline the translation steps** to Rust:
- Map C data structures to Rust equivalents.
- Adapt memory management to Rust's ownership and borrowing mechanisms.
- Address any C-specific features with appropriate Rust patterns.

3. **Provide the Rust translation** of the given C code:
- Use the given C and Rust symbols.
- Ensure functionality remains consistent.
- Use idiomatic Rust code.
- Include comments explaining key translation decision.

4. **Handling of C malloc, calloc, realloc, and free** of the given C code:
- When encountering `malloc`, `calloc`, `realloc`, `free`, use the corresponding implementation in Rust's `libc` crate.
- The functions `libc::malloc`, `libc::calloc`, `libc::realloc`, `libc::free`, etc., must be called correctly in Rust code within an Unsafe block.
"""
    prompt += "Only return the transalted Rust code and do not return any explanations."

    return prompt

def translate_header_information(c_context, random_test_json, translate_order):
    prompt = """Translate the C header information into corresponding Rust code. The C header information includes `structs`, `enums`, `unions`, `global variable definitions`, `typedef statements`, `Macros`, `function declarations`. Following these guidelines:
1. **Header Information**: Convert header code to Rust code. Include necessary struct, union, enum definitions, global variable definitions, typedef statements, ,macros, and function declarations.
2. **Structs**: Translate C strcuts to Rust structs. Use `#[repr(C)]` if C representation is needed. If a struct member is a pointer (e.g., int*), use Rust reference (&T) or smart pointers (e.g., Box, Rc, Arc) as appropriate.
3. **Unions**: Unions can be translated to Rust enums or structs, depending on the context.
4. **Enums**: The naming style of `enum` is identical to C code, including the naming of enumeration values.
5. **Macros**: Simple macros should be translated to Rust constants. Complex macros should be translated to Rust macros using macro_rules!.
6. **Global Variables**: Translate global variables to static or const variables in Rust. If the global variable is a pointer, use a reference or smart pointer in Rust.
7. **Typedefs**: Simplify type definitions. For pointer typedefs, use Rust references or smart pointers (e.g., Box<T>, Rc<T>, Arc<T>).
8. **Function Declarations**: Translate function parameters and return values from C types to Rust types. Convert pointer parameters and return values to Rust references or smart pointers.
9. **General Rules**: Avoid using libc or ffi types unless necessary for interoperability. Ensure that the translated code is safe and idiomatic Rust.
10. **Avoid Rust Keywords**: If any function names, parameter names, or variable names are Rust keywords, replace them with non-keyword alternatives. Use underscores _ or prefixes like c_ to avoid conflicts.
### Complex C Macro Definition Translation Example:
C Macro Definition:
```c
#include <stdio.h>

#define PRINT_MSG(msg) do { \\
    if (msg) { \\
        printf("Message: %s\\n", msg); \\
    } else { \\
        printf("No message provided.\\n"); \\
    } \\
} while (0)
```
Corresponding Rust Macro Definition:
```rust
macro_rules! print_msg {
    ($msg:expr) => {
        if !$msg.is_empty() {
            println!("Message: {}", $msg);
        } else {
            println!("No message provided.");
        }
    };
}
```
### Simple C Macro Definition Translation Example:
C Macro Definition:
```c
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
```
Corresponding Rust Macro Definition:
```rust
const PI: f64 = 3.141592653589793;
const MAX_BUFFER_SIZE: usize = 1024;
```

### C Function Declaration Translation Examples:
C Function Declarations:
```c
// Begin list of C function declarations
int add(int a, int b);
char* get_name();
// End list of C function declarations
```
Corresponding Rust Function Declarations:
```rust
fn add(a: i32, b: i32) -> i32 {
    // Empty
}

fn get_name() -> String {
    // Empty
}
```

### C Pointers and Rust Pointers Translation Examples:
Example 1:
C Pointer:
```c
void* void_ptr = NULL;
int value = 42;
void_ptr = &value;
int* int_ptr = (int*)void_ptr;
printf("%d\n", *int_ptr);
```
Rust Pointer:
```rust
use std::ffi::c_void;

fn main() {
    let value: i32 = 42;
    let void_ptr: *mut c_void = &value as *const i32 as *mut c_void;

    // Cast `*mut c_void` to `*mut i32`
    let int_ptr: *mut i32 = void_ptr as *mut i32;

    unsafe {
        println!("Value: {}", *int_ptr); // Output: Value: 42
    }
}
```
Example 2:
C Pointer:
```c
struct MyStruct {
    int a;
    double b;
};

struct MyStruct my_struct = {42, 3.14};
void* void_ptr = &my_struct;
struct MyStruct* struct_ptr = (struct MyStruct*)void_ptr;
printf("a: %d, b: %f\n", struct_ptr->a, struct_ptr->b);
```
Rust Pointer:
```rust
#[repr(C)]
struct MyStruct {
    a: i32,
    b: f64,
}

fn main() {
    let my_struct = MyStruct { a: 42, b: 3.14 };
    let void_ptr: *mut std::ffi::c_void = &my_struct as *const MyStruct as *mut std::ffi::c_void;

    // Cast `*mut c_void` to `*mut MyStruct`
    let struct_ptr: *mut MyStruct = void_ptr as *mut MyStruct;

    unsafe {
        println!("a: {}, b: {}", (*struct_ptr).a, (*struct_ptr).b); // Output: a: 42, b: 3.14
    }
}
```
Example 3:
C Pointer:
```c
int add(int a, int b) {
    return a + b;
}

void* func_ptr = (void*)add;
int (*add_ptr)(int, int) = (int (*)(int, int))func_ptr;
printf("%d\n", add_ptr(3, 4));
```
Rust Pointer:
```rust
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let func_ptr: fn(i32, i32) -> i32 = add;
    let void_ptr: *mut std::ffi::c_void = func_ptr as *mut std::ffi::c_void;

    // Cast `*mut c_void` back to fn(i32, i32) -> i32`
    let add_ptr: extern "C" fn(i32, i32) -> i32 = unsafe { std::mem::transmute(void_ptr) };

    println!("Result: {}", add_ptr(3, 4)); // Output: Result: 7
}
```
Example 4:
C Pointer:
```c
int arr[3] = {1, 2, 3};
void* void_ptr = arr;
int* int_ptr = (int*)void_ptr;
for (int i = 0; i < 3; i++) {
    printf("%d\n", int_ptr[i]);
}
```
Rust Pointer:
```rust
fn main() {
    let arr: [i32; 3] = [1, 2, 3];
    let void_ptr: *mut std::ffi::c_void = arr.as_ptr() as *mut std::ffi::c_void;

    // Cast `*mut c_void` to `*const i32`
    let int_ptr: *const i32 = void_ptr as *const i32;

    unsafe {
        for i in 0..3 {
            println!("{}", *int_ptr.add(i)); // Output: 1, 2, 3
        }
    }
}
```
Example 5:
C Pointer:
```c
double value = 3.14;
void* void_ptr = &value;
int* int_ptr = (int*)void_ptr;
printf("%d\n", *int_ptr);
```
Rust Pointer:
```rust
fn main() {
    let value: f64 = 3.14;
    let void_ptr: *mut std::ffi::c_void = &value as *const f64 as *mut std::ffi::c_void;

    // Cast `*mut c_void` to `*mut i32`
    let int_ptr: *mut i32 = void_ptr as *mut i32;

    unsafe {
        println!("Value: {}", *int_ptr); // Output: Value: 1074340347 (undefined behavior)
    }
}
```
Exmaple 6:
C Enums:
```c
typedef enum {
       BINARY_HEAP_TYPE_MIN,
       BINARY_HEAP_TYPE_MAX
} BinaryHeapType;
```
Rust Enums:
```rust
#[derive(Debug, Clone, Copy)]
enum BinaryHeapType {
    BINARY_HEAP_TYPE_MIN,
    BINARY_HEAP_TYPE_MAX,
}
```
The header code is shown as follows:
"""

    prompt += f"{c_context}\n"
    prompt += get_cot_instructions(header_cot_steps, True)
    print(f"Header Prompt:{prompt}")
    print(f"Prompt len:{len(prompt)}")
    print(f"Header:{random_test_json.replace('_output.json', '.h')}")
    #encoding = tiktoken.get_encoding('cl100k_base')
    #num_tokens = len(encoding.encode(prompt))
    #print(f"Tokens:{num_tokens}")
    maximum_request = 2
    request_idx = 0
    code_snippet = ""
    while request_idx < maximum_request:
        try:
            completion = openai_instance.chat.completions.create(
              model=LLM_MODEL,
              messages=[
                {"role": "system", "content": "You are now acting as an expert in C and Rust programming languages. You are good at translating C programs into Rust programs."},
                {"role": "user", "content": prompt}
              ],
            )
        except Exception as e:
            print(f"Exception while call LLM:{e}")
            request_idx += 1
            continue
        choice = completion.choices[0]
        message = choice.message
        code_snippet = message.content
        if "fn pancake(n: i32)" in code_snippet:
            break
        rust_file_name = "tmp_gen_outer.rs"
        with open(rust_file_name, "w") as rs_file:
            rs_file.write(code_snippet)
        llm_gen_filename = random_test_json.split(os.path.sep)[-1].replace("_output.json", f"_llmgen-{translate_order+1}.txt")
        llm_gen_path = os.path.join(llm_path, llm_gen_filename)
        with open(llm_gen_path, "w") as gf:
            gf.write(code_snippet)
        try:
            result = subprocess.run([f"./codeextractor/target/debug/codeextractor", rust_file_name], capture_output=True, text=True, timeout=10)
        except subprocess.TimeoutExpired:
            request_idx += 1
            continue
        if result.returncode != 0:
            print("Extracting code failed!")
            print(result.stderr)
            request_idx += 1
            continue
        else:
            code_snippet = result.stdout
            break
    print(f"Code Generated by llm:{code_snippet}")
    translate_order += 1
    return code_snippet, translate_order

def extract_rust_symbol_table(code_snippet, rust_symbol_table):
    # Extract Rust context information
    rust_context_file = "tmp_gen_context.rs"
    #print(f"Generated Rust code snippet:\n{code_snippet}")
    with open(rust_context_file, "w") as context_file:
        context_file.write(code_snippet)
        rust_context = code_snippet
    try:
        result = subprocess.run([f"./contextextractor/target/debug/contextextractor", rust_context_file], capture_output=True, text=True, timeout=10)
    except subprocess.TimeoutExpired:
        print(f"Timeout while extract context of :\n{code_snippet}") 
    if result.returncode != 0:
        print(f"Extracting context failed!\n{code_snippet}\n")
        print(result.stderr)
    else:
        rust_context_json = json.loads(result.stdout)
        try:
            for macro_info in rust_context_json['macros']:
                rust_symbol_table[macro_info['name']] = {
                    'category': 'macro',
                    'value': macro_info['definition'],
                }
        except Exception:
            print(f"Error to extract macros in Rust context!")
        
        try:
            for const_info in rust_context_json['constants']:
                rust_symbol_table[const_info['name']] = {
                    'category': 'global_var',
                    'type': const_info['ty'],
                    'init_value': const_info['value'],
                    'attr': 'const',
                }
        except Exception:
            print(f"Error to extract constants in Rust context!")
        
        try:
            for static_info in rust_context_json['statics']:
                rust_symbol_table[static_info['name']] = {
                    'category': 'global_var',
                    'type': static_info['ty'],
                    'init_value': static_info['value'],
                    'attr': 'static',
                }
        except Exception:
            print(f"Error to extract statics in Rust context!")
       
        try:
            for type_def in rust_context_json['types']:
                rust_symbol_table[type_def['name']] = {
                    'category': 'typedef',
                    'original': type_def['ty'],
                }
        except Exception:
            print(f"Error to extract types in Rust context!")

        try:
            for function in rust_context_json['functions']:
                name = function['name']
                inputs = ", ".join(f"{name}: {ty}" for name, ty in function['inputs'])
                output = function['output'] or "void"
                is_unsafe = function['is_unsafe']
                unsafe_str = "unsafe " if is_unsafe else ""
                function_params = [(name, ty) for name, ty in function['inputs']]
                rust_symbol_table[function['name']] = {
                    'category': 'function',
                    'return_type': function['output'],
                    'parameters': function_params,
                }
        except Exception:
            print(f"Error to extract functions in Rust context!")
           
        try:
            for struct in rust_context_json['structs']:
                attrs = struct.get('attrs', '')
                name = struct['name']
                fields = "\n    ".join(f"{name}: {ty}," for name, ty in struct['fields'])
                struct_members = dict()
                for name, ty in struct['fields']:
                    struct_members[name] = ty
                rust_symbol_table[struct['name']] = {
                    'category': 'struct',
                    'members': struct_members,
                }
        except Exception:
            print(f"Error to extract strcuts in Rust context!")
           
        try:
            for enum in rust_context_json['enums']:
                attrs = enum.get('attrs', '')
                name = enum['name']
                variants = "\n    ".join(
                    f"{variant['name']} {{ {', '.join(f'{name}: {ty}' for name, ty in variant['fields'])} }}"
                    for variant in enum['variants']
                )
                enum_members = list()

                for variant in enum['variants']:
                    enum_member_fields = []
                    for name, ty in variant['fields']:
                        enum_member_fields.append((name, ty))
                    rust_symbol_table[variant['name']] = {
                        'category': 'enum_member',
                        'enum':  enum['name'],
                        'fields': enum_member_fields,
                    }
                    if enum_member_fields:
                        fields_str = ",".join(enum_member_fields)
                        enum_members.append(f"{variant['name']}({fields_str})")
                    else:
                        enum_members.append(variant["name"])
                rust_symbol_table[name] = {
                    'category': 'enum',
                    'members': enum_members,
                }
        except Exception:
            print(f"Error to extract enums in Rust context!")

        try:
            for impl in rust_context_json['impls']:
                impl_name = impl['type_name']
                for function in impl['functions']:
                    name = function['name']
                    inputs = ", ".join(f"{name}: {ty}" for name, ty in function['inputs'])
                    output = function['output'] or "void"
                    is_unsafe = function['is_unsafe']
                    unsafe_str = "unsafe " if is_unsafe else ""
                    function_params = [(name, ty) for name, ty in function['inputs']]
                    rust_symbol_table[function['name']] = {
                        'category': 'impl method',
                        'return_type': function['output'],
                        'parameters': function_params,
                    }
        except Exception:
            print(f"Error to extract impls in Rust context!")

    return rust_symbol_table

def update_rust_symbol_table(json_file, rust_symbol_table):
    with open(json_file, "r") as jfile:
        data = json.load(jfile)
        if "Rust" not in data:
            data["Rust"] = dict()
        data["Rust"]["symbol_table"] = rust_symbol_table

    with open(json_file, "w") as jfile:
        json.dump(data, jfile, indent=4)

def update_rust_definitions(json_file, definitions):
    with open(json_file, "r") as jfile:
        data = json.load(jfile)
        if "Rust" not in data:
            data["Rust"] = {}
        data["Rust"]["header_definitions"] = definitions

    with open(json_file, "w") as jfile:
        json.dump(data, jfile, indent=4)

def is_empty_json_file(test_path):
    with open(test_path, "r") as tfile:
        data = json.load(tfile)

        if "C" not in data:
            return True
        else:
            if "header_definitions" not in data["C"]:
                if "code" not in data["C"]:
                    return True
            elif not data["C"]["header_definitions"]:
                if "code" not in data["C"]:
                    return True
                elif not data["C"]["code"]:
                    return True
            #elif "code" not in data["C"]:
            #    return True
            #elif not data["C"]["code"]:
            #    return True

    return False

# Read the src path to be passed while extracting C symbols.
src_path = sys.argv[1]

# Load C project files.
test_file_list = []
for test_file in os.listdir(test_path):
    test_file_json = os.path.join(test_path, test_file)
    # filter empty json file.
    if not is_empty_json_file(test_file_json):
        test_file_list.append(test_file)
    else:
        print(test_file_json)

test_file_list.sort()
test_file_list = ['arraylist_output.json']
test_idx = 0

# Iteratively translate .c file in C project one by one
while test_idx < len(test_file_list):
    # c and rust context information
    c_context = ""
    rust_context = ""
    c_symbol_table = dict()
    rust_symbol_table = dict()

    # get one of the test json file.
    test_file = test_file_list[test_idx]
    random_test_json = os.path.join(test_path, test_file)
    HEADER_SUCCEED = False
    HEADER_TRANSLATION = True 
    # if the file is translated, then we ignore it.
    if in_passed_files(pass_filename, random_test_json):
        print(f"{random_test_json} has been parsed!")
        continue

    # read the symbol table and header definition from test json file.
    with open(random_test_json, "r") as random_test_file:
        random_test_content = json.load(random_test_file)
        c_symbol_table = random_test_content["C"]["symbol_table"] if "symbol_table" in random_test_content["C"] else ""
        c_context = random_test_content["C"]["header_definitions"] if "header_definitions" in random_test_content["C"] else ""
        if "code" in random_test_content["C"]:
            HEADER_TRANSLATION = False
 
    print(f"Translating: {random_test_json}\n") 
    # translate the .h file
    translate_order = 0
    # translate the header definitions.
    if c_context and not HEADER_SUCCEED:
        rust_context, translate_order = translate_header_information(c_context, random_test_json, translate_order)
        rust_symbol_table = extract_rust_symbol_table(rust_context, rust_symbol_table)
        HEADER_SUCCEED = True

        print(f"Generated Rust header:\n{rust_context}")
        print(f"Generated Rust symbol table:\n{rust_symbol_table}")
    
    # Translate iteratively each function in C source file.
    # prompt = get_icl_prompt()
    prompt = ""
    # translate the .c file
    gen_rust_code = "" if not rust_context else rust_context
    if not HEADER_TRANSLATION:
        test_code_snippets = get_test_code_snippets(random_test_json)
    else:
        # translate the .h file
        test_code_snippets = []

    # add the rust definitions into prompt.
    '''
    # Do not use the c context and rust context every time. We only extract the related C and Rust symbols by parsing c code snippet.
    if rust_context:
        prompt += """
I have a C project with various `.h` and `.c` files. To facilitate conversion to Rust, I collect the header file, including structs, unions, enums, macros, global variable, typedefs, and function declarations.
The header file is shown as follows:
```c\n"""
        prompt += f"{c_context}"
        prompt += "\n```"
        prompt +="""
I translated header information into Rust, including structs, unions, enums, macros, global variables, and typedefs. I also created empty function definitions corresponding to C function declarations. Now, I want to translate the `.c` files into Rust, using the translated Rust file as context.
Please use the following Rust code as context to assist in translating the given C code into Rust. Ensure the Rust translation aligns with the types, structures, and symbols defined in the context.
```rust
"""
        prompt += f"{rust_context}"
        prompt += "```\n"
    '''
    prompt += """\nEach C code snippet is prefaced with "C code snippet `idx number`:". You must not include any additional explanations or information about the generated Rust code.\nThe C code snippet is shown below:\n"""
    
    old_prompt = prompt
    test_code_snippet_idx = 0
    MAXIMUM_LINE = 2
    MAIN_FLAG = get_main_flag(random_test_json) if not HEADER_TRANSLATION else False
    code_snippet = ""
    # get one function or several function which the body length is less than 50
    while test_code_snippet_idx < len(test_code_snippets):
        prompt = old_prompt
        code_len = 0
        code_snippet_num = 0
        while code_len < MAXIMUM_LINE and test_code_snippet_idx < len(test_code_snippets):
            test_code_snippet_block = test_code_snippets[test_code_snippet_idx]
            test_code_kind = test_code_snippet_block["kind"]
            test_code_snippet = test_code_snippet_block["code"]
            code_len += len(test_code_snippet.split("\n"))
            if code_len > MAXIMUM_LINE and code_snippet_num > 0:
                break
            prompt += f"""C Code snippet `{test_code_snippet_idx+1}`:\n```c\n{test_code_snippet}\n```\n"""
            prompt += get_rag_prompt(test_code_snippet)
            test_code_snippet_idx += 1
            code_snippet_num += 1
            if "symbols" not in test_code_snippet_block:
                continue
            print(f"symbols: {test_code_snippet_block['symbols']}, {len(c_symbol_table), len(rust_symbol_table)}")
            test_code_symbols = test_code_snippet_block["symbols"]
            # extract the global symbols used in the C code snippet.
            if len(c_symbol_table) > 0 and len(rust_symbol_table) > 0:
                try:
                    result = subprocess.run(['python3', 'extract-code-snippet-symbol.py', ":".join(test_code_symbols), json.dumps(c_symbol_table), json.dumps(rust_symbol_table)], capture_output=True, text=True, timeout=10)
                    if result.returncode != 0:
                        print(f"Fail to extrat test code snippet symbols:{result.stderr}-\n{result.stdout}")
                    if result.stdout:
                        prompt += "The C code snippet uses symbols like enum values, struct definitions, macro definitions, global variables, and function signatures. I've translalted these symbols into Rust to maintain consistency."
                        prompt += f"{result.stdout}\n"
                    else:
                        print("There is no common symbols in C and Rust!")
                except subprocess.TimeoutExpired:
                    pass
        prompt += get_cot_instructions(source_cot_steps, False)
        print(f"Prompt:{prompt}")
        print(f"Prompt len:{len(prompt)}")
        print(f"Test:{random_test_json}")
        #encoding = tiktoken.get_encoding('cl100k_base')
        #num_tokens = len(encoding.encode(prompt))
        #print(f"Tokens:{num_tokens}")
        maximum_request = 2
        request_idx = 0
        code_snippet = ""
        while request_idx < maximum_request:
            try:
                completion = openai_instance.chat.completions.create(
                  model=LLM_MODEL,
                  messages=[
                    {"role": "system", "content": "You are now acting as an expert in C and Rust programming languages. You are good at translating C programs into Rust programs."},
                    {"role": "user", "content": prompt}
                  ],
                )
            except Exception as e:
                print(f"Exception while call LLM:{e}")
                request_idx += 1
                continue
            choice = completion.choices[0]
            message = choice.message
            code_snippet = message.content
            if "fn pancake(n: i32)" in code_snippet:
                break
            rust_file_name = "tmp_gen_outer.rs"
            with open(rust_file_name, "w") as rs_file:
                rs_file.write(code_snippet)
            llm_gen_file = test_file.replace("_output.json", f"_llmgen-{translate_order+1}.txt")
            llm_gen_path = os.path.join(llm_path, llm_gen_file)
            with open(llm_gen_path, "w") as gf:
                gf.write(code_snippet)
            try:
                result = subprocess.run([f"./codeextractor/target/debug/codeextractor", rust_file_name], capture_output=True, text=True, timeout=10)
            except subprocess.TimeoutExpired:
                request_idx += 1
                continue
            if result.returncode != 0:
                print("Extracting code failed!")
                print(result.stderr)
                request_idx += 1
                continue
            else:
                code_snippet = result.stdout
                break
        if "fn pancake(n: i32)" in code_snippet:
            continue
        print(f"Code Generated by llm:\n{code_snippet}")
        #gen_rust_code, code_snippet = check_symbol(gen_rust_code, code_snippet)
        
        # Check single rust file
        import time
        max_iterations = 2
        syntax_idx = 0
        SYNTAX_CORRECT = False
        while syntax_idx < max_iterations:
            print("The generated code:\n", code_snippet)
            has_error, error = check_rust_code(code_snippet)
            llm_gen_file = test_file.replace("_output.json", f"_llmcode-{translate_order+1}.txt")
            llm_gen_path = os.path.join(llm_path, llm_gen_file)
            with open(llm_gen_path, "w") as gf:
                gf.write(code_snippet) 
            if not has_error:
                #if MAIN_FLAG:
                #    rust_pro_single_file_name = os.path.join(rust_pro_name, "src", "main.rs")
                #else:
                #    rust_pro_single_file_name = os.path.join(rust_pro_name, "src", test_file.replace("_output.json", ".rs"))
                #with open(rust_pro_single_file_name, "w", encoding="utf-8") as c_rust:
                #    print(code_snippet, file=c_rust)
                llm_gen_file = test_file.replace("_output.json", f"_llmsyntax-{translate_order+1}.txt")
                llm_gen_path = os.path.join(llm_path, llm_gen_file)
                with open(llm_gen_path, "w") as gf:
                    gf.write(code_snippet) 
                syntax_pass_file.write(f"{translate_order+1}-{random_test_json}\n")
                syntax_pass_file.flush()
                SYNTAX_CORRECT = True
                gen_rust_code += code_snippet
                break
            else:
                print(f"Error to check rust code snippet:{error}")
                if isinstance(error, str):
                    break
                code_snippet = fix_errors_with_llm(error, code_snippet)
                print(f"Corrected rust code:\n")
                print(code_snippet)
                with open(rust_file_name, "w", encoding="utf-8") as c_rust:
                    print(code_snippet, file=c_rust)
            syntax_idx += 1
        if not SYNTAX_CORRECT and syntax_idx == max_iterations:
            gen_rust_code += code_snippet
        translate_order += 1
    test_idx += 1
    rust_pro_code_name = test_file.replace("_output.json", ".rs") if not MAIN_FLAG else "main.rs"
    # replace '-' to '_'
    rust_pro_code_name = rust_pro_code_name.replace("-", "_")
    
    if rust_pro_code_name.endswith("_rs"):
        rust_pro_code_name = rust_pro_code_name[:-3] + ".rs"
    rust_pro_file = os.path.join(rust_pro_name, "src", rust_pro_code_name)

    rust_code_decl_file = "tmp_rust_code_decl.rs"
    with open(rust_code_decl_file, "w") as tmp_decl_file:
        tmp_decl_file.write(rust_context)
    tmp_rust_context = rust_context

    tmp_gen_rust_code = gen_rust_code
    rust_code_source_file = "tmp_rust_code_source.rs"
    with open(rust_code_source_file, "w") as tmp_source_file:
        tmp_source_file.write(gen_rust_code)

    try:
        result = subprocess.run([f"./remove-funcdecl/target/debug/remove-funcdecl", rust_code_source_file, rust_code_decl_file], capture_output=True, text=True, timeout=10)
    except subprocess.TimeoutExpired:
        print(f"Timeout while formatting rust context of :\n{rust_context}")
    if result.returncode != 0:
        print(f"Failed to format rust context code!\n{code_snippet}\n")
        print(result.stderr)
    else:
        gen_rust_code = result.stdout

    with open(rust_code_source_file, "w") as tmp_source_file:
        tmp_source_file.write(gen_rust_code)

    try:
        result = subprocess.run([f"./merge-impl/target/debug/merge-impl", rust_code_source_file], capture_output=True, text=True, timeout=10)
    except subprocess.TimeoutExpired:
        print(f"Timeout while formatting rust context of :\n{rust_context}")
    if result.returncode != 0:
        print(f"Failed to format rust context code!\n{code_snippet}\n")
        print(result.stderr)
    else:
        gen_rust_code = result.stdout

    if not HEADER_TRANSLATION:
        with open(rust_pro_file, "w") as rust_w_file:
            #gen_rust_code = tmp_rust_context + tmp_gen_rust_code
            rust_w_file.write(gen_rust_code)
    else:
        with open(rust_pro_file, "w") as rust_w_file:
            rust_w_file.write(rust_context)
    update_rust_symbol_table(random_test_json, rust_symbol_table)
